extends Control

const default_config: Dictionary = {
	fps = 60.0,
	game_up = KEY_UP,
	game_down = KEY_DOWN,
	game_left = KEY_LEFT,
	game_right = KEY_RIGHT,
	game_restart = KEY_R,
	game_view_left = KEY_A,
	game_view_right = KEY_D,
}

const default_keys: Array = [
	"game_up", "game_down", "game_left", "game_right",
	"game_restart", "game_view_left", "game_view_right"
]

var press_any_key_name: String = ""

var config: Dictionary = {}
var new_config: Dictionary = {}

var current_level_scene: PackedScene = null
var current_level: Spatial = null

var menu_stack: Array = []

onready var game_menu: Control = $GameMenu

onready var vpc: ViewportContainer = $ViewportContainer
onready var vp: Viewport = $ViewportContainer/Viewport
onready var vpc2: ViewportContainer = $ViewportContainer/Viewport/ViewportContainer
onready var vp2: Viewport = $ViewportContainer/Viewport/ViewportContainer/Viewport

onready var ap: AnimationPlayer = $AnimationPlayer

export(float) var blur_size: float = 0.0 setget set_blur_size

func load_config():
	var f: File = File.new()
	if f.open("user://config.cfg", File.READ) == OK:
		var v = str2var(f.get_as_text())
		if v is Dictionary:
			var v0: Dictionary = default_config.duplicate(true)
			for k in v:
				v0[k] = v[k]
			config = v0

func save_config():
	var f: File = File.new()
	if f.open("user://config.cfg", File.WRITE) == OK:
		f.store_string(var2str(config))

func apply_config():
	Engine.target_fps = clamp(config.get("fps", 60), 12, 120) as int

	for k in default_keys:
		InputMap.action_erase_events(k)
		var iek = InputEventKey.new()
		iek.scancode = config[k]
		InputMap.action_add_event(k, iek)

func show_config():
	new_config = config.duplicate(true)

	var gc: GridContainer = $GameMenu/OptionsMenu/ScrollContainer/GridContainer

	gc.get_node("fps").value = new_config["fps"]

	for k in default_keys:
		gc.get_node(k).text = OS.get_scancode_string(new_config[k])

func set_blur_size(p):
	blur_size = p

func _ready():
	blur_size = 0.0

	config = default_config.duplicate(true)
	load_config()
	apply_config()

	var actions: Array = [
		[$GameMenu/MainMenu/VBoxContainer/Start, {
			type = "Menu",
			menu = $GameMenu/SelectLevel,
		}],
		[$GameMenu/MainMenu/VBoxContainer/Options, {
			type = "Menu",
			menu = $GameMenu/OptionsMenu,
		}],
		[$GameMenu/MainMenu/VBoxContainer/Exit, {
			type = "Exit",
		}],
		[$GameMenu/SelectLevel/Back, {
			type = "Back",
		}],
		[$GameMenu/OptionsMenu/HBoxContainer/OK, {
			type = "ConfigOK",
		}],
		[$GameMenu/OptionsMenu/HBoxContainer/Cancel, {
			type = "Back",
		}],
		[$GameMenu/PausedMenu/VBoxContainer/Continue, {
			type = "Back",
		}],
		[$GameMenu/PausedMenu/VBoxContainer/Restart, {
			type = "Restart",
		}],
		[$GameMenu/PausedMenu/VBoxContainer/RestartFromCheckpoint, {
			type = "RestartFromCheckpoint",
		}],
		[$GameMenu/PausedMenu/VBoxContainer/SelectLevel, {
			type = "Menu",
			menu = $GameMenu/SelectLevel,
		}],
		[$GameMenu/PausedMenu/VBoxContainer/Options, {
			type = "Menu",
			menu = $GameMenu/OptionsMenu,
		}],
		[$GameMenu/PausedMenu/VBoxContainer/Exit, {
			type = "Exit",
		}],
		[$GameMenu/PressAnyKey/Panel/VBoxContainer/Cancel, {
			type = "PressAnyKeyCancel",
		}],
	]

	var level_list: Array = [
		"TestLevel", "TestLevel2", "TestBallSpeed", "TestCubeMap",
	]

	# add levels
	var level_container: VBoxContainer = $GameMenu/SelectLevel/ScrollContainer/VBoxContainer
	for level_name in level_list:
		var btn: Button = Button.new()
		btn.text = level_name
		level_container.add_child(btn)
		actions.append([btn, {
			type = "Level",
			file = "res://levels/%s.tscn" % level_name,
		}])

	# add config controls
	var gc: GridContainer = $GameMenu/OptionsMenu/ScrollContainer/GridContainer

	for k in default_keys:
		var lbl: Label = Label.new()
		lbl.text = k
		gc.add_child(lbl)
		var btn: Button = Button.new()
		btn.name = k
		btn.size_flags_horizontal = SIZE_EXPAND_FILL
		gc.add_child(btn)
		actions.append([btn, {
			type = "PressAnyKey",
			name = k,
		}])

	# connect signals
	for action in actions:
		action[0].connect("pressed", self, "_on_button_pressed", [action[1]])

	show_menu($GameMenu/MainMenu, false)

func _on_button_pressed(param):
	match param.type:
		"Menu":
			if param.menu == $GameMenu/OptionsMenu:
				show_config()
			show_menu(param.menu)
		"Exit":
			get_tree().quit()
		"Back":
			menu_back()
		"Level":
			load_level(param.file)
		"Restart":
			restart_level()
		"PressAnyKey":
			press_any_key_name = param.name
			$GameMenu.pause_mode = PAUSE_MODE_STOP
			$GameMenu/PressAnyKey.show()
		"PressAnyKeyCancel":
			press_any_key_update()
		"ConfigOK":
			new_config["fps"] = $GameMenu/OptionsMenu/ScrollContainer/GridContainer/fps.value as int
			config = new_config.duplicate(true)
			save_config()
			apply_config()
			menu_back()
		_:
			# TODO:
			print("TODO: %s" % param)

func press_any_key_update(new_key = null):
	if new_key != null:
		new_config[press_any_key_name] = new_key
		$GameMenu/OptionsMenu/ScrollContainer/GridContainer.get_node( \
			press_any_key_name).text = OS.get_scancode_string(new_key)

	press_any_key_name = ""
	$GameMenu.pause_mode = PAUSE_MODE_INHERIT
	$GameMenu/PressAnyKey.hide()

func load_level(file_name):
	var ps: PackedScene = load(file_name)
	if ps == null:
		return

	current_level_scene = ps

	restart_level()

func restart_level():
	hide_menu()

	if current_level != null:
		current_level.queue_free()
		current_level = null

	if current_level_scene != null:
		current_level = current_level_scene.instance()
		if current_level != null:
			current_level.pause_mode = PAUSE_MODE_STOP
			vp2.add_child(current_level)

func hide_menu():
	# clear menu stack
	menu_stack = []

	# hide menu
	for o in game_menu.get_children():
		o.hide()
	game_menu.hide()

	# disable blur effect
	ap.stop(true)
	blur_size = 0.0

	# resume game
	get_tree().paused = false
	vp.gui_disable_input = false

func show_menu(obj: Control, can_back: bool = true):
	# pause game if there is any
	get_tree().paused = true
	vp.gui_disable_input = true

	# start blur effect
	if current_level != null and blur_size < 1e-3 and not ap.is_playing():
		ap.play("blur")

	# add to stack
	menu_stack.append([obj, can_back])

	# show menu
	for o in game_menu.get_children():
		o.visible = (o == obj)

	game_menu.show()

func menu_back(force_back: bool = false):
	if menu_stack.empty():
		hide_menu()
		return

	# check if we can back
	if not (force_back or menu_stack.back()[1]):
		return

	if menu_stack.size() <= 1:
		hide_menu()
		return

	menu_stack.pop_back()
	var obj: Control = menu_stack.back()[0]

	# show menu
	for o in game_menu.get_children():
		o.visible = (o == obj)

	game_menu.show()

func _input(event):
	if not press_any_key_name.empty():
		if event.is_action_pressed("ui_cancel"):
			press_any_key_update()
		elif event is InputEventKey:
			press_any_key_update(event.scancode)
			get_tree().set_input_as_handled()
		return

	if event.is_action_pressed("ui_cancel"):
		if menu_stack.empty():
			show_menu($GameMenu/PausedMenu)
		else:
			menu_back()

	if event.is_action_pressed("game_restart") and menu_stack.empty():
		restart_level()

# warning-ignore:unused_argument
func _process(delta):
	var stretch_shrink: float = max(1.0, blur_size)
	var log2_ss: float = log(stretch_shrink) * 1.5

	if blur_size > 1e-3:
		if vpc2.material == null:
			vpc2.material = ShaderMaterial.new()
			vpc2.material.shader = preload("res://blur.shader")
		if vpc.material == null:
			vpc.material = ShaderMaterial.new()
			vpc.material.shader = preload("res://blur.shader")

		vp2.get_texture().flags = Texture.FLAG_FILTER | Texture.FLAG_MIPMAPS
		vp.get_texture().flags = Texture.FLAG_FILTER

		vpc2.material.set_shader_param("bias", log2_ss)
		vpc2.material.set_shader_param("blur_vector", Vector2(blur_size / max(1.0, rect_size.x), 0.0))
		vpc.material.set_shader_param("blur_vector", Vector2(0.0, blur_size / max(1.0, rect_size.y)))

	else:
		vp2.get_texture().flags = 0
		vp.get_texture().flags = 0

		vpc2.material = null
		vpc.material = null
